#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Modbus RTU模拟器
基于pymodbus 3.11.2实现，参考modbus_rtu.py示例
"""

import random
import time
import threading
from typing import Optional, Callable

from pymodbus.server import StartSerialServer, ServerStop
from pymodbus.datastore import ModbusServerContext, ModbusDeviceContext
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus import ModbusDeviceIdentification, FramerType


class ModbusRtuSimulator:
    """Modbus RTU设备模拟器 - 独立实现"""

    def __init__(self, message_callback: Optional[Callable] = None):
        """
        初始化RTU模拟器

        Args:
            message_callback: 消息回调函数 (message, level, msg_type)
        """
        self.start_slave_id = None
        self.callback = message_callback or self._default_callback
        self.running = False
        self.server_thread = None
        self.update_thread = None

        # 服务器配置
        self.port = None
        self.baudrate = 9600
        self.databits = 8
        self.parity = 'N'
        self.stopbits = 1
        self.slave_count = 0
        self.start_address = 0
        self.register_count = 0
        self.update_interval = 2

        # 服务器上下文
        self.slave_contexts = {}
        self.context = None
        self.identity = None

    def _default_callback(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """默认回调"""
        print(f"[{level}] {message}")

    def _notify(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """发送通知"""
        self.callback(message, level, msg_type)

    def _create_slave_context(self, register_count: int) -> ModbusDeviceContext:
        """创建单个从站的数据存储区"""
        return ModbusDeviceContext(
            di=ModbusSequentialDataBlock(0, [0] * 10),
            co=ModbusSequentialDataBlock(0, [0] * 10),
            hr=ModbusSequentialDataBlock(0, [0] * register_count),
            ir=ModbusSequentialDataBlock(0, [0] * 10),
        )

    def _create_device_identity(self) -> ModbusDeviceIdentification:
        """创建设备标识"""
        identity = ModbusDeviceIdentification()
        identity.VendorName = 'IoT-Util'
        identity.ProductCode = 'ModbusRTUSim'
        identity.ProductName = 'Modbus RTU Simulator'
        identity.ModelName = 'RTU Model'
        identity.MajorMinorRevision = '1.0'
        return identity

    def configure(self, port: str, baudrate: int, databits: int, parity: str,
                  stopbits: int, slave_count: int, start_slave_id: int, start_address: int, register_count: int,
                  update_interval: int) -> bool:
        """
        配置RTU模拟器

        Args:
            port: 串口端口
            baudrate: 波特率
            databits: 数据位
            parity: 校验位
            stopbits: 停止位
            slave_count: 从站数量
            start_slave_id: 开始从站地址
            start_address: 开始寄存器地址
            register_count: 每个从站的寄存器数量
            update_interval: 数据更新间隔（秒）

        Returns:
            配置是否成功
        """
        try:
            self.port = port
            self.baudrate = baudrate
            self.databits = databits
            self.parity = parity
            self.stopbits = stopbits
            self.slave_count = slave_count
            self.start_slave_id = start_slave_id
            self.start_address = start_address
            self.register_count = register_count
            self.update_interval = update_interval

            # 创建多从站上下文
            self.slave_contexts = {
                slave_id: self._create_slave_context(register_count)
                for slave_id in range(self.start_slave_id, self.start_slave_id + slave_count)
            }

            # 创建服务器上下文
            self.context = ModbusServerContext(devices=self.slave_contexts, single=False)

            # 创建设备标识
            self.identity = self._create_device_identity()

            self._notify(
                f"✅ RTU模拟器配置成功: {port}@{baudrate}, {slave_count}个从站",
                "SUCCESS",
                "normal"
            )
            return True

        except Exception as e:
            self._notify(f"❌ RTU模拟器配置失败: {e}", "ERROR", "error")
            return False

    def start(self) -> bool:
        """启动RTU模拟器"""
        if self.running:
            self._notify("⚠️ RTU模拟器已在运行中", "WARNING", "normal")
            return True

        if not self.context:
            self._notify("❌ 请先配置RTU模拟器", "ERROR", "error")
            return False

        try:
            self.running = True

            # 启动数据更新线程
            self.update_thread = threading.Thread(
                target=self._update_registers_loop,
                daemon=True
            )
            self.update_thread.start()

            # 启动RTU服务器线程
            self.server_thread = threading.Thread(
                target=self._run_server,
                daemon=True
            )
            self.server_thread.start()

            self._notify(f"🚀 RTU模拟器启动成功: {self.port}@{self.baudrate}", "SUCCESS", "normal")
            return True

        except Exception as e:
            self.running = False
            self._notify(f"❌ RTU模拟器启动失败: {e}", "ERROR", "error")
            return False

    def _run_server(self):
        """运行RTU服务器（阻塞）"""
        try:
            self._notify(f"📡 RTU服务器在 {self.port} 启动", "INFO", "normal")

            # 启动服务器（阻塞运行）
            # Note: pymodbus 3.11.2的StartSerialServer不支持allow_reuse_address参数
            StartSerialServer(
                context=self.context,
                identity=self.identity,
                port=self.port,
                baudrate=self.baudrate,
                framer=FramerType.RTU,
                stopbits=self.stopbits,
                bytesize=self.databits,
                parity=self.parity
            )

        except Exception as e:
            self._notify(f"❌ RTU服务器运行错误: {e}", "ERROR", "error")
        finally:
            self.running = False

    def _update_registers_loop(self):
        """更新寄存器数据循环"""
        self._notify("🔄 开始更新RTU模拟器寄存器...", "INFO", "normal")

        while self.running:
            try:
                # 遍历所有从站更新寄存器
                # 关键修复：直接从context中获取slave_context来更新
                # 而不是使用self.slave_contexts，避免引用不一致问题
                for slave_id, slave_context in self.slave_contexts.items():
                    # 生成随机数据
                    values = [random.randint(0, 65535) for _ in range(self.register_count - 1)]
                    # 更新保持寄存器
                    slave_context.setValues(3, self.start_address, values)
                    # 记录日志
                    self._notify(
                        f"📊 RTU从站{slave_id}: HR[0-{self.register_count-1}]={values}",
                        "INFO",
                        "normal"
                    )

                time.sleep(self.update_interval)

            except Exception as e:
                self._notify(f"❌ RTU寄存器更新错误: {e}", "ERROR", "error")
                time.sleep(self.update_interval)

    def stop(self) -> bool:
        """停止RTU模拟器"""
        try:
            self.running = False
            self._notify(f"🔌 RTU模拟器 {self.port} 正在停止...", "INFO", "normal")

            ServerStop()
            # 等待线程结束
            time.sleep(0.5)

            self._notify(f"⏹️ RTU模拟器已停止", "INFO", "normal")
            return True

        except Exception as e:
            self._notify(f"❌ RTU模拟器停止失败: {e}", "ERROR", "error")
            return False

    def is_running(self) -> bool:
        """检查是否运行中"""
        return self.running
